package com.android.serialport;

import android.util.Log;

import com.android.serialport.entity.InventoryBean;
import com.android.serialport.itf.DataListener;
import com.android.serialport.itf.ParseDataListener;
import com.android.serialport.utils.ByteUtils;
import com.rfid.DealDataRunnable;
import com.rfid.analysis.Handler;
import com.rfid.analysis.HandlerAddress;
import com.rfid.analysis.HandlerCRC;
import com.rfid.analysis.HandlerChain;
import com.rfid.analysis.HandlerCommand;
import com.rfid.analysis.HandlerData;
import com.rfid.analysis.HandlerHeader;
import com.rfid.analysis.HandlerLength;
import com.rfid.analysis.HandlerStatus;
import com.rfid.analysis.Message;
import com.rfid.event.ReadEvent;
import com.rfid.event.WriteEvent;
import com.rfid.protocol.Instruction;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;

/**
 * @author Administrator
 * @date 2021/2/1
 */
public class RFIDDevice extends SerialPortDevice implements ParseDataListener {
    private Map<String, InventoryBean> mInventoryBeanMap = new ConcurrentHashMap<>();
    private List<String> mUidList = new ArrayList<>();
    private int mBlockDataIndex = 0;
    private int mWriteFlagIndex = 0;
    private boolean writeFlagIsBorrow = true;
    private DealDataRunnable mDealDataRunnable;
    private volatile boolean isStopInventory = false;
    private List<InventoryBean> mNeedWriteFlagList = new ArrayList<>();

    public RFIDDevice(String path, int baudRate, int stopBits, int dataBits, int parity, int flowCon, int flags) {
        super(path, baudRate, stopBits, dataBits, parity, flowCon, flags);
        List<Instruction> mCommandList = new ArrayList<>();
        mCommandList.add(new InventoryCommand(this));
        mCommandList.add(new DataBlockCommand(this));
        mCommandList.add(new WriteFlagCommand(this));

        Message message = new Message();
        HandlerChain chain = new HandlerChain();
        Handler head = new HandlerHeader();
        chain.addHandler(head)
                .addHandler(new HandlerLength())
                .addHandler(new HandlerAddress())
                .addHandler(new HandlerCommand(mCommandList))
                .addHandler(new HandlerStatus())
                .addHandler(new HandlerData())
                .addHandler(new HandlerCRC())
                .addHandler(head);
        message.setFirstHandler(head);
        message.setCurrentNodeHandler(head);
        Executors.newSingleThreadExecutor().submit(mDealDataRunnable = new DealDataRunnable(message));

        setDataListener(new DataListener() {
            @Override
            public void receive(byte[] data) {
                Log.e("receive", "接收数据=" + ByteUtils.bytesToHexString(data));
                mDealDataRunnable.putData(data);
            }

            @Override
            public void send(byte[] data) {
                Log.e("send", "发送的数据=" + ByteUtils.bytesToHexString(data));
            }
        });
    }

    public void startInventory() {
        isStopInventory = false;
        reInventory();
    }


    public void stopInventory() {
        isStopInventory = true;
    }

    private void getBlockData(String uid) {
        write(getBlockDataCommand(uid));
    }

    public void writeFlag(List<InventoryBean> list, boolean isBorrow) {
        mNeedWriteFlagList.clear();
        writeFlagIsBorrow = isBorrow;
        mWriteFlagIndex = 0;
        if (list != null) {
            for (InventoryBean bean : list) {
                if (bean.isSucceed()) {
                    mNeedWriteFlagList.add(bean);
                }
            }
        }
        if (!mNeedWriteFlagList.isEmpty()) {
            write(getWriteFlagCommand(mNeedWriteFlagList.get(0).uid));
        } else {
            EventBus.getDefault().post(new WriteEvent());
        }
    }


    @Override
    public void onData(String command, byte[] data) {
        switch (command) {
            case "Inventory":
                if (data == null || data.length == 0) {
                    //盘点结束
                    Log.e("Inventory", "盘点结束");
                    if (!mUidList.isEmpty()) {
                        //查询到UID，根据uid查询数据块
                        Log.e("Inventory", "开始获取数据块，index=" + mBlockDataIndex);
                        getBlockData(mUidList.get(0));
                    } else {
                        //没有盘点到数据,继续盘点
                        Log.e("Inventory", "没有盘点到数据,继续盘点");
                        reInventory();
                    }
                } else if (data.length > 1) {
                    Log.e("Inventory", "盘点未结束");
                    byte[] uid = Arrays.copyOfRange(data, 1, data.length);
                    String sUid = ByteUtils.bytesToHexString(uid);
                    if (!mInventoryBeanMap.containsKey(sUid)) {
                        mInventoryBeanMap.put(sUid, new InventoryBean(sUid));
                        mUidList.add(sUid);
                    }
                }
                break;
            case "DataBlock":
                mBlockDataIndex = mBlockDataIndex + 1;
                if (data != null && data.length > 0) {
                    Log.e("DataBlock", "查询数据块有数据");
                    InventoryBean bean = parseBlockData(data);
                    if (bean != null) {
                        EventBus.getDefault().post(new ReadEvent(bean));
                    }
                }
                if (mBlockDataIndex < mUidList.size()) {
                    //继续查询卡号
                    Log.e("DataBlock", "继续查询数据块,index=" + mBlockDataIndex);
                    getBlockData(mUidList.get(mBlockDataIndex));
                } else {
                    //卡号查询完成,继续盘点
                    Log.e("DataBlock", "继续盘点");
                    reInventory();
                }
                break;
            case "WriteFlag":
                Log.e("WriteFlag", "标志写入完成");
                mWriteFlagIndex = mWriteFlagIndex + 1;
                if (mWriteFlagIndex < mNeedWriteFlagList.size()) {
                    write(getWriteFlagCommand(mNeedWriteFlagList.get(mWriteFlagIndex).uid));
                } else {
                    EventBus.getDefault().post(new WriteEvent());
                }
                break;
            default:
                break;
        }
    }

    /**
     * 再次盘点
     */
    private void reInventory() {
        if (!isStopInventory) {
            try {
                mBlockDataIndex = 0;
                mUidList.clear();
                mInventoryBeanMap.clear();
                write(ByteUtils.hexToByteArr("EC07FFFE0100C1B4"));
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private byte[] getBlockDataCommand(String uid) {
        String command = "11FFFE2300" + uid + "0003";
        command = "EC" + command + ByteUtils.CRC16(command);
        return ByteUtils.hexToByteArr(command);
    }

    private byte[] getWriteFlagCommand(String uid) {
        String command = "10FFFE2701" + uid + (writeFlagIsBorrow ? "c1" : "07");
        command = "EC" + command + ByteUtils.CRC16(command);
        return ByteUtils.hexToByteArr(command);
    }

    private InventoryBean parseBlockData(byte[] data) {
        InventoryBean inventoryBean = null;
        try {
            byte[] uid = new byte[8];
            byte[] cardNo = new byte[7];
            System.arraycopy(data, 0, uid, 0, 8);
            System.arraycopy(data, 13, cardNo, 0, 2);
            System.arraycopy(data, 16, cardNo, 2, 4);
            System.arraycopy(data, 21, cardNo, 6, 1);
            StringBuilder tStringBuf = new StringBuilder();
            char[] charsNo = new char[7];
            for (int i = 0; i < 7; i++) {
                charsNo[i] = (char) cardNo[i];
            }
            tStringBuf.append(charsNo);
            String sUid = ByteUtils.bytesToHexString(uid);
            if (mInventoryBeanMap.containsKey(sUid)) {
                inventoryBean = mInventoryBeanMap.get(sUid);
                String strNo = tStringBuf.toString();
                inventoryBean.setBookBarNo(strNo);
                Log.e("parseBlockData", strNo);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return inventoryBean;
    }

}